/*
 * Copyright (c) 2025 Industrial Software Feature Database
 */
package com.comac.ins.isfd.controller;

import cn.dev33.satoken.annotation.SaCheckPermission;
import com.comac.ins.common.core.domain.R;
import com.comac.ins.common.core.exception.base.BaseException;
import com.comac.ins.common.core.validate.AddGroup;
import com.comac.ins.common.idempotent.annotation.RepeatSubmit;
import com.comac.ins.common.log.annotation.Log;
import com.comac.ins.common.log.enums.BusinessType;
import com.comac.ins.common.mybatis.core.page.TableDataInfo;
import com.comac.ins.common.web.core.BaseController;
import com.comac.ins.isfd.constant.enums.IsfdImportMethodEnum;
import com.comac.ins.isfd.domain.IsfdDatabaseDataLogicalDeleteOne;
import com.comac.ins.isfd.domain.IsfdDatabaseMongoQuery;
import com.comac.ins.isfd.domain.bo.*;
import com.comac.ins.isfd.domain.bo.query.IsfdDatabaseDataImportRecordQueryBo;
import com.comac.ins.isfd.domain.vo.*;
import com.comac.ins.isfd.service.IIsfdDatabaseDataManagementProService;
import com.comac.ins.isfd.service.IIsfdDatabaseDataManagementService;
import com.comac.ins.isfd.service.IIsfdDatabaseModelConfigService;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.bson.Document;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.*;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.ParseException;
import java.time.Instant;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * 数据库建模后，数据表数据管理
 *
 * @author Lion Li
 * @date 2024-11-27
 */
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/isfd/database/data")
@Slf4j
public class IsfdDatabaseDataManagementController extends BaseController {

    private final IIsfdDatabaseDataManagementService isfdDatabaseDataManagementService;
    private final IIsfdDatabaseModelConfigService isfdDatabaseModelConfigService;
    private final IIsfdDatabaseDataManagementProService isfdDatabaseDataManagementProService;

    /**
     * 导入结果-数据分页列表查询
     */
    @SaCheckPermission("isfd:databaseData:list")
    @PostMapping("/queryPageList")
    public TableDataInfo<IsfdDatabaseDataImportRecordListVo> queryPageList(@RequestBody IsfdDatabaseDataImportRecordQueryBo queryBo) {
        return isfdDatabaseDataManagementService.queryPageList(queryBo);
    }

    /**
     * 导入结果-下载接口
     */
    @SaCheckPermission("isfd:databaseData:list")
    @GetMapping("/download")
    public void download(@RequestParam("recordId") Long recordId, HttpServletResponse response) throws IOException {
        IsfdDatabaseDataImportRecordVo recordVo = isfdDatabaseDataManagementService.queryRecordVoById(recordId);
        if (recordVo == null || !IsfdImportMethodEnum.canDownload(recordVo.getImportMethod())) {
            log.warn("无效下载请求 recordId={}", recordId);
            response.sendError(HttpStatus.BAD_REQUEST.value(), "当前导入记录不能下载或者无数据，请联系管理员确认后重试！");
            return;
        }

        // 移除硬编码路径（生产环境必须删除）
        String dataFileUrl = recordVo.getDataFileUrl();
        Path filePath = Paths.get(dataFileUrl);

        if (!Files.exists(filePath)) {
            log.error("文件不存在: {}", filePath);
            response.sendError(HttpStatus.NOT_FOUND.value(), "文件不存在: " + filePath.getFileName());
            return;
        }

        // 获取文件名并编码处理中文
        String fileName = filePath.getFileName().toString();
        String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8)
            .replace("+", "%20");

        // 设置响应头
        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION,
            "attachment; filename=\"" + encodedFileName + "\"; filename*=UTF-8''" + encodedFileName);
        response.setHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, HttpHeaders.CONTENT_DISPOSITION);

        // 使用缓冲区高效复制文件
        try (InputStream is = Files.newInputStream(filePath);
             OutputStream os = response.getOutputStream()) {

            byte[] buffer = new byte[8192]; // 8KB 缓冲区
            int bytesRead;
            while ((bytesRead = is.read(buffer)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            os.flush();
        } catch (Exception e) {
            log.error("文件下载失败: {} | {}", recordId, fileName, e);
            if (!response.isCommitted()) {
                response.sendError(HttpStatus.INTERNAL_SERVER_ERROR.value(), "文件下载失败");
            }
        }
    }

    /**
     * 导入XML数据
     */
    @SaCheckPermission("isfd:databaseData:importXML")
    @Log(title = "XML数据导入", businessType = BusinessType.INSERT)
    @RepeatSubmit()
    @PostMapping("/importXML")
    public R<Void> importXMLData(@Validated IsfdDatabaseDataImportXMLBo bo,
                                 @RequestParam("file") MultipartFile file) {
        return toAjax(isfdDatabaseDataManagementService.importXMLData(bo, file));
    }

    /**
     * 数据检查
     */
    @SaCheckPermission("isfd:databaseData:import")
    @Log(title = "插入数据", businessType = BusinessType.INSERT)
    @RepeatSubmit()
    @PostMapping(value = "/import/check", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public R<IsfdDatabaseDataImportCheckVo> importDataCheck(@RequestParam("modelId") Long modelId,
                                                            @RequestParam(value = "parentId", required = false) String parentId,
                                                            @RequestParam(value = "importMethod", required = false) String importMethod,
                                                            @RequestParam("file") MultipartFile file) {
        return R.ok(isfdDatabaseDataManagementService.checkAndCacheImportData(modelId, parentId, importMethod, file));
    }

    /**
     * 确认批量插入数据
     */
    @SaCheckPermission("isfd:databaseData:import")
    @Log(title = "插入数据", businessType = BusinessType.INSERT)
    @RepeatSubmit()
    @PostMapping("/import/confirm")
    public R<Void> confirmImport(@RequestBody @Validated(AddGroup.class) IsfdDatabaseDataImportRecordBo importRecordBo) {
        return toAjax(isfdDatabaseDataManagementService.confirmImportData(importRecordBo));
    }

    /**
     * 导出JSON格式数据
     */
    @SaCheckPermission("isfd:databaseData:export")
    @Log(title = "导出JSON数据", businessType = BusinessType.EXPORT)
    @GetMapping("/export/json")
    public R<Map<String, Object>> exportJsonData(@RequestParam("modelId") Long modelId) {
        return R.ok(isfdDatabaseDataManagementProService.exportDataAsJson(modelId));
    }

    /**
     * 导出JSON格式数据
     */
    @SaCheckPermission("isfd:databaseData:export")
    @Log(title = "导出JSON数据", businessType = BusinessType.EXPORT)
    @GetMapping("/export/file")
    public ResponseEntity<Resource> exportJsonFile(@RequestParam("modelId") Long modelId) throws IOException {
        Map<String, Object> jsonData = isfdDatabaseDataManagementProService.exportDataAsJson(modelId);

        // 将Map转换为JSON字符串
        ObjectMapper objectMapper = new ObjectMapper();
        byte[] jsonBytes = objectMapper.writeValueAsBytes(jsonData);
        ByteArrayResource resource = new ByteArrayResource(jsonBytes);

        // 设置下载头
        ContentDisposition contentDisposition = ContentDisposition.attachment()
            .filename(modelId + ".json", StandardCharsets.UTF_8)
            .build();

        return ResponseEntity.ok()
            .header(HttpHeaders.CONTENT_DISPOSITION, contentDisposition.toString())
            .contentType(MediaType.APPLICATION_JSON)
            .contentLength(jsonBytes.length)
            .body(resource);
    }

    @SaCheckPermission("isfd:databaseData:export")
    @Log(title = "导出JSON数据", businessType = BusinessType.EXPORT)
    @GetMapping("/export/zip")
    public ResponseEntity<Resource> exportJsonZip(@RequestParam("modelId") Long modelId) throws IOException {


//        Map<String, Object> jsonData = isfdDatabaseDataManagementProService.exportDataAsJson(modelId);

        List<Document> dataAll = isfdDatabaseDataManagementService.queryDataAll(modelId, new IsfdDatabaseMongoQuery());
        Map<String, Object> jsonData = new HashMap<>();
        jsonData.put("data", dataAll);

        // 将Map转换为JSON并打包为ZIP
        ObjectMapper objectMapper = new ObjectMapper();
        byte[] jsonBytes = objectMapper.writeValueAsBytes(jsonData);

        // 创建ZIP输出流
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try (ZipOutputStream zos = new ZipOutputStream(baos)) {
            ZipEntry entry = new ZipEntry(modelId + ".json");
            zos.putNextEntry(entry);
            zos.write(jsonBytes);
            zos.closeEntry();
        }

        ByteArrayResource resource = new ByteArrayResource(baos.toByteArray());

        // 设置ZIP下载头
        ContentDisposition contentDisposition = ContentDisposition.attachment()
            .filename(modelId + ".zip", StandardCharsets.UTF_8)
            .build();

        return ResponseEntity.ok()
            .header(HttpHeaders.CONTENT_DISPOSITION, contentDisposition.toString())
            .contentType(MediaType.parseMediaType("application/zip"))  // 修改媒体类型
            .contentLength(baos.size())
            .body(resource);
    }

    /**
     * 检查数据变更
     */
    @SaCheckPermission("isfd:databaseData:check")
    @Log(title = "检查数据变更", businessType = BusinessType.OTHER)
    @GetMapping("/checkDataChanges")
    public R<DataChangeResultVo> checkDataChanges(
        @RequestParam Long modelId,
        @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) Instant startTime,
        @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) Instant endTime) throws ParseException {

        System.out.println(
            "startDate: " + startTime.toString() +
                ", endDate: " + endTime.toString()
        );
        DataChangeResultVo result = isfdDatabaseDataManagementProService.checkDataChanges(modelId, startTime.toString(), endTime.toString());

        return R.ok(result);
    }

    /**
     * 查询所有数据
     */
    @SaCheckPermission("isfd:databaseData:list")
    @GetMapping("/queryAll")
    public R<List<Document>> queryDataAll(@RequestParam("modelId") Long modelId) {
        return R.ok(isfdDatabaseDataManagementService.queryDataAll(modelId, new IsfdDatabaseMongoQuery()));
    }

    /**
     * 查询一条完整数据
     */
    @SaCheckPermission("isfd:databaseData:queryOne")
    @PostMapping("/queryOne")
    public R<List<Object>> queryDataOne(@RequestBody(required = false) IsfdDatabaseDataListQueryBo queryCondition) {
        String tableName = queryCondition.getTableName();
        String objectId = queryCondition.getObjectId();

        @SuppressWarnings("unchecked")
        List<Object> data = (List<Object>) isfdDatabaseDataManagementService.queryDataDetail(tableName, objectId, queryCondition).get(0);
        return R.ok(data);
    }

    /**
     * 查询数据树结构
     */
    @SaCheckPermission("isfd:databaseData:tree")
    @PostMapping("/show")
    public R<IsfdDatabaseDataPageResultVo> queryDataShow(@RequestBody IsfdDatabaseDataTreeQueryBo queryCondition) {
        return R.ok(isfdDatabaseDataManagementService.queryDataShowAsTableOrTree(queryCondition));
    }

    /**
     * 保存文件类数据
     */
    @SaCheckPermission("isfd:databaseData:file")
    @PostMapping("/saveFile")
    public R<String> saveFile(@RequestParam("tableName") String tableName,
                              @RequestParam("file") MultipartFile file) {
        return R.ok(isfdDatabaseDataManagementService.saveFileAndReturnAddress(tableName, file));
    }

    /**
     * 更新一条数据
     */
    @SaCheckPermission("isfd:databaseData:updateOne")
    @PostMapping("/updateOne")
    public R<Void> updateOne(@Validated @RequestBody IsfdDatabaseDataInsertOneBo bo) {
        String tableName = bo.getTableName();
        String objectId = bo.getObjectId();
        List<Object> data = bo.getData();

        return toAjax(isfdDatabaseDataManagementService.updateData(tableName, objectId, data));
    }

    /**
     * 新增一条数据
     */
    @SaCheckPermission("isfd:databaseData:insertOne")
    @PostMapping("/insertOne")
    public R<Void> insertOne(@Validated @RequestBody IsfdDatabaseDataInsertOneBo bo) {
        String tableName = bo.getTableName();
        List<Object> data = bo.getData();
        return toAjax(isfdDatabaseDataManagementService.insertData(tableName, data));
    }

    /**
     * 逻辑删除一条数据
     */
    @SaCheckPermission("isfd:databaseData:deleteOne")
    @DeleteMapping("/deleteOne")
    public R<Void> deleteOne(@RequestBody IsfdDatabaseDataLogicalDeleteOne deleteOne) {
        String tableName = deleteOne.getTableName();
        String objectId = deleteOne.getObjectId();
        return toAjax(isfdDatabaseDataManagementService.logicDeleteData(tableName, objectId));
    }

    /**
     * 恢复一条逻辑删除数据
     */
    @SaCheckPermission("isfd:databaseData:recoverOne")
    @PostMapping("/recoverOne")
    public R<Void> recoverOne(@RequestBody IsfdDatabaseDataLogicalDeleteOne recoverOne) {
        String tableName = recoverOne.getTableName();
        String objectId = recoverOne.getObjectId();
        return toAjax(isfdDatabaseDataManagementService.recoverLogicDeleteData(tableName, objectId));
    }

    /**
     * 获取数据导入模板
     */
    @SaCheckPermission("isfd:database:data:template")
    @GetMapping("/template")
    public ResponseEntity<Resource> getTemplate(@RequestParam("modelId") Long modelId) throws IOException {

        Resource templateBytes = isfdDatabaseModelConfigService.downloadTemplate(modelId);
        // 确保资源有效
        if (templateBytes == null || templateBytes.contentLength() == 0) {
            throw new BaseException("模板文件为空或不存在！");
        }
        return ResponseEntity.ok()
            .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + (templateBytes.getFilename() != null ? templateBytes.getFilename() : "template.zip") + "\"")
            .contentType(MediaType.parseMediaType("application/zip"))
            .body(templateBytes);
    }


    /**
     * 删除数据
     */
    @SaCheckPermission("isfd:databaseData:delete")
    @Log(title = "删除数据", businessType = BusinessType.DELETE)
    @RepeatSubmit()
    @DeleteMapping("/delete/byRecordId")
    public R<Void> remove(@NotNull(message = "recordId不能为空")
                          @RequestParam(name = "recordId") Long recordId) {
        return toAjax(isfdDatabaseDataManagementService.deleteDataByRecordId(recordId));
    }

    /**
     * 清空模型内所有数据
     */
    @SaCheckPermission("isfd:databaseData:delete")
    @Log(title = "删除数据", businessType = BusinessType.DELETE)
    @RepeatSubmit()
    @DeleteMapping("/{modelId}")
    public R<Void> clearData(@NotNull(message = "modelId不能为空")@PathVariable Long modelId,
                             @RequestParam(required = false, name = "recordId") Long recordId) {
        return toAjax(isfdDatabaseDataManagementService.clearDataByModelId(modelId, recordId));
    }
}
